﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IEnumerator=System.Collections.IEnumerator;

namespace SynoManager
{
    /// <summary>
    /// This class implements a generic ringbuffer, with a specified capacity
    /// </summary>
    /// <typeparam name="T">The Type of items that are contained within the RingBuffer</typeparam>
    public class RingBuffer<T> : IEnumerable<T>
    {
        int capacity;
        T[] items;
        int pointer = 0;

        /// <summary>
        /// Delegate defining the method signature for the <see cref="ItemAdded" /> event.
        /// </summary>
        /// <param name="sender">The object that has thrown the event</param>
        /// <param name="item">The item that was added</param>
        public delegate void ItemAddedEventHandler(object sender, T item);
        /// <summary>
        /// Thrown when a new item is added to the buffer
        /// </summary>
        public event ItemAddedEventHandler ItemAdded;

        /// <summary>
        /// Create a new RingBuffer
        /// </summary>
        /// <param name="capacity">The capacity of the RingBuffer</param>
        public RingBuffer(int capacity)
        {
            this.capacity = capacity;
            items = new T[capacity];
        }

        /// <summary>
        /// Add a new item
        /// </summary>
        /// <param name="item">The item to add</param>
        public void Add(T item)
        {
            if (items[pointer] != null)
                if (items[pointer] is IDisposable)
                    ((IDisposable)items[pointer]).Dispose();

            items[pointer] = item;
            pointer = (pointer + 1) % capacity;

            if (ItemAdded != null)
                ItemAdded(this, item);
        }

        /// <summary>
        /// The array of items that are currently in the array
        /// </summary>
        public T[] Items
        {
            get
            {
                T[] result = new T[capacity];

                for (int i = 0; i < capacity; i++)
                    result[i] = items[((pointer + i) % capacity)];

                return result;
            }
        }

        /// <summary>
        /// Retrieve the item at the specified index
        /// </summary>
        /// <param name="index">The zero-based index</param>
        /// <returns>The item at the specified index</returns>
        public T this[int index]
        {
            get
            {
                /* index    value
                 * 0        D3
                 * 1        E4
                 * 2        A0
                 * 3        B1
                 * 4        C2
                 * pointer == 3
                 * value = (pointer + index) % size
                 */
                return items[((pointer + index) % capacity)];
            }
            set
            {
                items[((pointer + index) % capacity)] = value;
            }
        }

        /// <summary>
        /// The capacity of the RingBuffer
        /// </summary>
        public int Capacity
        { get { return capacity; } }

        /// <summary>
        /// Resize the RingBuffer, keeping all existing items in the array
        /// </summary>
        /// <param name="newSize">The new capacity for the buffer</param>
        public void Resize(int newSize)
        {
            T[] newArray = new T[newSize];

            for (int i = 0; (i < newSize) && (i < capacity); i++)
                newArray[i] = items[((pointer + i) % capacity)];

            pointer = 0;
            items = newArray;
            capacity = newSize;
        }

        /// <summary>
        /// Check if the array contains a null-value
        /// </summary>
        /// <returns>True if one of the items in the array is null</returns>
        public bool ContainsNull()
        {
            foreach (T item in items)
            {
                if (item == null)
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Enumerator to cycle through the items in the RingBuffer
        /// </summary>
        /// <returns>An enumerator</returns>
        public IEnumerator GetEnumerator()
        {
            for (int i = 0; i < capacity; i++)
            {
                int index = (i + pointer) % capacity;
                if (items[index] != null)
                    yield return items[index];
            }
        }

        #region IEnumerable<T> Members

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            for (int i = 0; i < capacity; i++)
            {
                int index = (i + pointer) % capacity;
                if (items[index] != null)
                    yield return items[index];
            }
        }

        #endregion
    }
}
